#!/usr/bin/perl


#=====================================================================
#      I N D E X  .  P L
#
#       Captivator, a "captive portal" style authentication system.
#
#       Copyright 2005 The University of Wisconsin Board of Regents
#       Licensed and distributed under the terms of the Perl Artistic 
#       License, see doc/LICENSE for details.
#
#       Written by: Dale W. Carder, dwcarder@doit.wisc.edu
#       Network Services Group
#       Division of Information Technology
#       University of Wisconsin at Madison
#
#       For more about Captivator-gw, see:
#       http://net.doit.wisc.edu/~dwcarder/captivator-gw
#
#======================================================================

#======================================================================
#    C O N F I G U R A T I O N   S E C T I O N
#

my $Config = "/usr/local/Captivator/etc/captivator.config";
use lib '/usr/local/Captivator/lib';

#
#======================================================================

#======================================================================
#	U S E   A N D   R E Q U I R E

use CGI qw(:standard);
use CGI::Carp qw(fatalsToBrowser);
use Captivator;

#
#======================================================================


#-----   O K,  A N D  G O !   -----------------------------------


# read in the config file
eval readConfig($Config);


# get things passed to us from apache, and taint check them.
my $q = new CGI;
my $user = untaint_usernm($q->param("username"));
my $pass = untaint_passwd($q->param("password"));
my $email = untaint_usernm($q->param("email"));
my $redirect = $q->param("redir");
my $remotehost = matchIpAddr($ENV{'REMOTE_ADDR'});

# debug output
my $gotstr = "index.pl: got remotehost: $remotehost";

if (defined($user)) {
	$gotstr = $gotstr . ", user: $user";
}

if (defined($redirect)) {
	$gotstr = $gotstr . ", redirect: $redirect";
}

slog(7,$gotstr);

# this is some terrible hackery that i'm not proud of 
# to deal with a redirect url containing query variables from the http get
my $fixed_redir;

if ($ENV{'REQUEST_METHOD'} eq 'GET') {
	my $querystr = $ENV{'QUERY_STRING'};
	my @redir_url = split(/\%22/,$querystr);
	$fixed_redir = $redir_url[1];
	slog(7,"fixed_redir: " . $fixed_redir );
}

my $redir;

# use the correct redirect variable.
# use the fixed one first, if that exists, otherwise use
# the supplied one, otherwise use the default

if (defined($fixed_redir)) { 
	$redir = $fixed_redir;
} elsif (defined($redirect)) { 
	$redir = $redirect;
} else { 
	$redir = $DefaultRedirect; 
}

# print http header
print("Content-Type: text/html;\nCache-Control: no-cache\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\n\n");

# see if their IP is coming from a vlan we are running
# if not, then we should not give them a login page.
my $vlan = ip2VlanNum($remotehost);

if (! $vlan ) {
	slog(4,"index.pl: host $remotehost not in the range we allow to log in");

	# print error page
	printSysErrPage();
	exitScript();
}

my $serverhost = getServerHost($vlan);
slog(7,"index.pl: serving $remotehost - $user from $serverhost");

# see if they are allready logged in.  If so, just give them the info page
my ($loggedinuser,$authmethod) = whoIsLoggedIn($remotehost);
if ($loggedinuser eq $user) {
        if($authmethod eq 'temporary') {
            slog(6,"index.pl: user $loggedinuser logged in as temporary");
        } else {
   	    slog(6,"index.pl: user $loggedinuser, $remotehost is already logged in");
	    printAllreadyLoggedInPage();
	    exitScript();
        }
} elsif ($loggedinuser ne 0) {
	slog(6,"index.pl: $loggedinuser already logged into $remotehost");
	printAllreadyLoggedInPage();
        exitScript();
}

my $from_email = 0;
# if we weren't given information via POST method, then they want the login form
# unless this was sent from an email, which passes the from_email=yes argument
if ($ENV{'REQUEST_METHOD'} ne 'POST') {
        unless($q->param("from_email")) {
        	slog(7,"index.pl: giving login form to $remotehost");
	        printLoginForm($redir,$serverhost);
        	exitScript();
        } else {
		$from_email = 1;
	}
}


#if email is defined, give them the registration form
if ( defined($email) ) {
        #if they've put in uniqname, password, and email, treat the email as an accident and undefine it
        if(defined($user) && $user ne '' && defined($pass) && $pass ne '') {
            $email = undef;
        } else {
            #make sure password is cleared so that we can use that to detect auth mechanism
            $user = $email;
            $pass = undef;
            slog(7,"index.pl: Adding temporary user $remotehost");
        }
# if both username and password are not specifed, give them the login form again
} elsif ( (! defined($user)) || (! defined($pass)) ) {
	slog(4,"index.pl: $remotehost didn't specify a username ($user) or a password");
	printLoginForm($redir,$serverhost);
	exitScript();
}


my $authenticated = 0;
if($pass) {
    $authenticated = authenticateUser($user,$pass);
} else {
 	if($email =~ /@umich.edu/) {
 		printInvalidEmailPage();
		slog(3,"$email tried to register as a guest.");
 		exitScript();
 	}
	$authenticated = validateAndAddEmail($email);
}

# ok, if we got this far, try to authenticate the user
if ( $authenticated ) {
	# then authentication was successful
	
        # if they were previously logged in as a different user 
        # i.e. temporary and have just clicked the email link
        # then attempt to remove their other access first
        if($authmethod ne 0) {
            my $userid = findUserId($remotehost); 
            if($userid) {
		slog(6,"index.pl: Trying to log out previous user $userid on $remotehost");
                insertIntoExpireUsers($userid,'expired'); #should make a new reason i guess
                removeHosts();
            } else { #this might just mean they've been expired since the script stated
                slog(5,"index.pl: can't find user_id to expire temp user $user.");
            }
        } 

	# get their mac address
	my $mac = getMacFromArpTable($remotehost);

	# in case for some reason we can't get their mac address
	if (!$mac) {
		slog(4,"index.pl: can't get mac address for user: $user, v4ip: $remotehost");
		printSysErrPage();
		exitScript();
	}
        #Make sure they're not blocked
        my @authz = checkAuthZ($user,$mac);
        unless($authz[0]) {
                slog(4,"index.pl: user $user blocked by Captivator AuthZ due to $authz[1]");
                printBlockedPage($authz[1]);
                exit;
        }

	
	slog(7,"index.pl: authentication successful for user: $user, v4ip: $remotehost, mac: $mac");

	#for testing make superusers who can access all ports
	my %superusers;
	$superusers{nbach} = 1;

        my $authmethod;
	if ($superusers{$user}) {
		$authmethod = 'superuser';
        } elsif( defined($pass) ) {
	        if($user =~ /@/) {
        	        $authmethod = 'guest';
                } else {
                	$authmethod = 'radius';
                }
        } else {
        	$authmethod = 'temporary';
        }


	# try to put them in the firewall
	if (tableInsert($remotehost,$mac,$vlan,$authmethod)) {

		slog(7,"index.pl: added firewall entry ok");

		# ok, that worked, so put them in the sql table
		
		my $user_id=0;

		$user_id = addToCurrentUsers($user,$remotehost,$mac,$vlan,$authmethod);

        	if (! ($user_id)) {
		
			slog(3,"index.pl: Couldn't add ($user,$remotehost,$mac,$vlan,$authmethod) to the SQL table!");

			# we couldn't add them to the sql table, so we have a problem.  
			# So, we need to remove them from the fw:

			tableDelete($remotehost,$mac,$vlan,$authmethod);
			
			# print system error page
			printSysErrPage();
			exitScript();
		}

		slog(4,"index.pl: allowed through firewall - user: $user, id: $user_id, v4ip: $remotehost, mac: $mac");

		# IF WE GET HERE, LIFE IS GOOD!
                if(defined($pass)) {
        	    # send radius accounting start messsage
	            radiusAcct($user,$user_id,$mac,1);
                    slog(7,"index.pl: redirecting $user back to $redir");
		    printRedirectPage($redir,$authmethod,$from_email);
                    exitScript();
                } else {
                    printGuestNoticePage($redir);
                    exitScript();
                }
	} else { 

		# we couldn't put them in the firewall for some reason
		slog(3,"index.pl: Couldn't add ($user,$remotehost,$mac,$vlan) to the firewall!");
		printSysErrPage();
		exitScript();
	}

} else {

	# otherwise the authentication was not successful
	slog(4,"index.pl: Authentication failed for user: $user, v4ip: $remotehost");

	printLoginFailedPage($redir,$serverhost);
	exitScript();
}


# just in case
slog(3,"index.pl didn't really end all that cleanly!");
exitScript(0);

sub exitScript {
    rpcDisconnect();
    my $code = shift;
    if($code) { 
        exit($code);
    } else {
        exit();
    }
}

